/*
 * @Author: 李石
 * @Date: 2024-05-20 08:09:08
 * @LastEditors: lishi
 * @LastEditTime: 2024-07-18 15:33:47
 * @Description: 
 * Copyright (c) 2024 by ${lishi0105@163.com}, All Rights Reserved. 
 */
#include "time_util.h"

#include <stdlib.h>
#include <sys/time.h>
#include <time.h>

#include <chrono>
#include <ctime>
#include <iomanip>
#include <sstream>
#include <string>


#if defined(WIN32) || defined (_WIN32) || defined (__WIN64)
#include <windows.h>
uint64_t TimeUtil::GetMillTimestamp() 
{
    LARGE_INTEGER frequency;        
    LARGE_INTEGER ticks;   
    QueryPerformanceFrequency(&frequency);
    QueryPerformanceCounter(&ticks);
    uint64_t microseconds = (ticks.QuadPart * 1000) / frequency.QuadPart;
    return microseconds;
}
#else 
#include <time.h>
uint64_t TimeUtil::GetMillTimestamp() 
{
    struct timespec ts;
    clock_gettime(CLOCK_REALTIME, &ts);
    return (uint64_t)(ts.tv_sec) * 1000 + ts.tv_nsec / 1000000;
}
#endif

uint64_t TimeUtil::GetMicroTimestamp() {
    struct timeval tp;
    gettimeofday(&tp, NULL);
    return ((uint64_t)(tp.tv_sec * 1000000 + tp.tv_usec));
}

uint64_t TimeUtil::GetSecondTimestamp() {
    struct timeval tp;
    gettimeofday(&tp, NULL);
    return (uint64_t)tp.tv_sec;
}

std::string TimeUtil::CurrentDatetime(const std::string &format) {
    auto ts = GetMicroTimestamp();
    if (format.empty()) {
        return std::to_string(ts);
    }
    std::time_t t = ts / 1000000;
    std::tm tm = *std::localtime(&t);
    char buffer[80] = {0};
    strftime(buffer, 80, format.c_str(), &tm);
    return std::string(buffer);
}

std::string TimeUtil::MillTsToTimeString(const unsigned long long msec, bool is_24hour) 
{
    uint64_t timestamp = msec;
    if (msec > 1e15){
        timestamp = msec/1000;
    }
    time_t t = timestamp / 1000;
    struct tm tm = *localtime(&t);
    int millisec = timestamp % 1000;
    char buffer[128] = {0};

    if (is_24hour) {
        sprintf(buffer, "%d-%02d-%02d %02d:%02d:%02d.%03d", 
                tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, 
                tm.tm_hour, tm.tm_min, tm.tm_sec, millisec);
    } else {
        const char* period = (tm.tm_hour < 12) ? "AM" : "PM";
        int hour = tm.tm_hour % 12;
        if (hour == 0) hour = 12;
        sprintf(buffer, "%d-%02d-%02d %02d:%02d:%02d.%03d %s", 
                tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, 
                hour, tm.tm_min, tm.tm_sec, millisec, period);
    }

    return std::string(buffer);
}

uint64_t TimeUtil::TimeStringToMilliseconds(const std::string& datetime) 
{
    if (datetime.length() < 19) {
        throw std::invalid_argument("Input string is not in the correct format: YYYY-MM-DD HH:MM:SS.sss");
    }

    std::tm t = {};
    std::istringstream ss(datetime.substr(0, 19)); // Only parse the date and time part
    ss >> std::get_time(&t, "%Y-%m-%d %H:%M:%S");

    if (ss.fail()) {
        throw std::invalid_argument("Failed to parse datetime string");
    }

    int milliseconds = 0;
    if(datetime.length() == 23){
        std::string milliseconds_str = datetime.substr(20, 3);
        try {
            milliseconds = std::stoi(milliseconds_str);
        } catch (const std::exception& e) {
            throw std::invalid_argument("Failed to parse milliseconds part");
        }
    }
    std::time_t time_epoch = std::mktime(&t);
    if (time_epoch == -1) {
        throw std::runtime_error("Failed to convert to time_t");
    }

    uint64_t timestamp_ms = static_cast<uint64_t>(time_epoch) * 1000 + milliseconds;
    return timestamp_ms;
}

bool TimeUtil::ifDayChanged()
{
    struct timeval tv;
    struct tm *tm_info;
    static int32_t last_day = -1;
    bool bChanged = false;
    gettimeofday(&tv, NULL);
    time_t current_sec = tv.tv_sec;

    // 将秒数转换为本地时间
    tm_info = localtime(&current_sec);
    int current_day = tm_info->tm_yday;  // 一年中的第几天

    // 判断天数是否发生变化
    if (last_day != -1 && current_day != last_day) {
        bChanged = true;
    }
    last_day = current_day;
    bChanged = false;
    return bChanged;
}

uint64_t TimeUtil::get_start_of_day_timestamp(uint32_t days_ago)
{
    struct timeval tv;
    struct tm tm_info;
    time_t current_sec, target_sec;
    long long target_millisec;

    gettimeofday(&tv, NULL);
    current_sec = tv.tv_sec;

    localtime_r(&current_sec, &tm_info);

    tm_info.tm_hour = 0;
    tm_info.tm_min = 0;
    tm_info.tm_sec = 0;
    tm_info.tm_isdst = -1; 
    target_sec = mktime(&tm_info);
    target_sec -= days_ago * 24 * 3600;
    target_millisec = (long long)target_sec * 1000L;
    return target_millisec;
}